/*
 *  Filename:      meos_timer.c
 *  Author:         Erick Huang<erickhuang1989@gmail.com>
 *  Revised:        2014-12-16
 *  Description:   Timer definition and manipulation functions.
 *
 *  Copyright (c) Erick Huang. All rights reserved.
 */
#include "meos_api.h"

typedef union {
  	uint32 time32;
  	uint16 time16[2];
  	uint8 time8[4];
} Time_t;

typedef struct
{
  	LIST   node;
  	Time_t timeout;
  	uint16 event_flag;
  	uint8  task_id;
  	uint32 reloadTimeout;
} meos_timer_rec_t;

#define MEOS_TIMER_TIMEOUT(node_ptr)           ( (meos_timer_rec_t *)(node_ptr)->timeout )
#define MEOS_TIMER_TIMEOUT32(node_ptr)         ( (meos_timer_rec_t *)(node_ptr)->timeout.timer32 )
#define MEOS_TIMER_EVENT_FLAG(node_ptr)        ( (meos_timer_rec_t *)(node_ptr)->event_flag )
#define MEOS_TIMER_TASK_ID(node_ptr)           ( (meos_timer_rec_t *)(node_ptr)->task_id )
#define MEOS_TIMER_RELOAD_TIMEOUT(node_ptr)    ( (meos_timer_rec_t *)(node_ptr)->reloadTimeout )




// Timer Pool Definitions
static LIST timer_head;

// Milliseconds since last reboot
static uint32 meos_systemClock;


void meos_timer_init( void )
{
  	meos_systemClock = 0;
	list_init(&timer_head);
}

uint32 meos_get_sys_clock( void )
{
  	return meos_systemClock;
}

static meos_timer_rec_t *find_timer( uint8 task_id, uint16 event_flag )
{
	LIST *search;

  	// Head of the timer list
	search = timer_head.next;

	while (search != &timer_head)
	{
		if (MEOS_TIMER_EVENT_FLAG(search) == event_flag &&
                 MEOS_TIMER_TASK_ID(search) == task_id)
		{
			break;
		}

		search = search->next;
	}

	if (search != &timer_head)
		return (meos_timer_rec_t *)search;
	else
		return (meos_timer_rec_t *)NULL;
}

static void delete_timer( meos_timer_rec_t *rmTimer )
{
  	// Does the timer list really exist
  	if ( rmTimer )
  	{
    	// Clear the event flag and meos_timer_update() will delete
    	// the timer from the list.
    	rmTimer->event_flag = 0;
  	}
}

static meos_timer_rec_t * add_timer( uint8 task_id, uint16 event_flag, uint32 timeout )
{
  	meos_timer_rec_t *newTimer;

  	// Look for an existing timer first
  	newTimer = find_timer( task_id, event_flag );
  	if ( newTimer )
  	{
    	// Timer is found - update it.
    	newTimer->timeout.time32 = timeout;

    	return newTimer;
  	}
  	else
  	{
    	// New Timer
    	newTimer = meos_mem_alloc( sizeof( meos_timer_rec_t ) );

    	if ( newTimer )
    	{
      		// Fill in new timer
      		newTimer->task_id = task_id;
      		newTimer->event_flag = event_flag;
      		newTimer->timeout.time32 = timeout;
      		newTimer->reloadTimeout = 0;
			list_init_node(&newTimer->node);

			// Add it to the tail of the timer list
			list_insert_tail(&timer_head, &newTimer->node);

      		return newTimer;
    	}
    	else
    	{
      		return (meos_timer_rec_t *)NULL;
    	}
  	}
}

Status_t meos_start_timerEx( uint8 taskID, uint16 event_id, uint32 timeout_value )
{
  	halIntState_t intState;
  	meos_timer_rec_t *newTimer;

  	HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.

  	// Add timer
  	newTimer = add_timer( taskID, event_id, timeout_value );

  	HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.

  	return ( (newTimer != NULL) ? SUCCESS : FAILURE);
}

Status_t meos_start_reload_timerEx( uint8 taskID, uint16 event_id, uint32 timeout_value )
{
  	halIntState_t intState;
  	meos_timer_rec_t *newTimer;

  	HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.

  	// Add timer
  	newTimer = add_timer( taskID, event_id, timeout_value );
  	if ( newTimer )
  	{
		// Load the reload timeout value
		newTimer->reloadTimeout = timeout_value;
  	}

  	HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.

  	return ( (newTimer != NULL) ? SUCCESS : FAILURE);
}

Status_t meos_stop_timerEx( uint8 task_id, uint16 event_id )
{
  	halIntState_t intState;
  	meos_timer_rec_t *foundTimer;

  	HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.

  	// Find the timer to stop
  	foundTimer = find_timer( task_id, event_id );
  	if ( foundTimer )
  	{
    	delete_timer( foundTimer );
  	}

  	HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.

  	return ( (foundTimer != NULL) ? SUCCESS : FAILURE);
}

void meos_timer_update( uint32 updateTime )
{
	LIST *srch;
	halIntState_t intState;

  	Time_t timeUnion;
  	timeUnion.time32 = updateTime;

  	HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.
  	// Update the system time
  	meos_systemClock += updateTime;

	srch = timer_head.next;
	while (srch != &timer_head)
	{
		meos_timer_rec_t *freeTimer = NULL;

        if (MEOS_TIMER_TIMEOUT32(srch) > timeUnion.time32)
        {
			MEOS_TIMER_TIMEOUT32(srch) -= timeUnion.time32;
        }
        else
        {
          	MEOS_TIMER_TIMEOUT32(srch) = 0;
        }

		// Check for reloading
		if ((MEOS_TIMER_TIMEOUT32(srch) == 0) && MEOS_TIMER_RELOAD_TIMEOUT(srch) && MEOS_TIMER_EVENT_FLAG(srch))
		{
			// Notify the task of a timeout
        	meos_set_event( MEOS_TIMER_TASK_ID(srch), MEOS_TIMER_EVENT_FLAG(srch));

        	// Reload the timer timeout value
        	MEOS_TIMER_TIMEOUT32(srch) = MEOS_TIMER_RELOAD_TIMEOUT(srch);
		}

		// When timeout(timeout == 0) or delete (event_flag == 0)
		if ((MEOS_TIMER_TIMEOUT32(srch) == 0) || (MEOS_TIMER_EVENT_FLAG(srch) == 0))
		{
			// Take out of list
			list_delete(srch);
			freeTimer = (meos_timer_rec_t *)srch;
		}

		// Free node.
		if ( freeTimer )
      	{
        	if ( freeTimer->timeout.time32 == 0 )
        	{
          		meos_set_event( freeTimer->task_id, freeTimer->event_flag );
        	}
        	meos_mem_free( (void *)freeTimer );
      	}

		srch = srch->next;
	}

	HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.
}

uint32 meos_next_timeout( void )
{
	LIST *srch;
  	uint32 nextTimeout = MEOS_TIMERS_MAX_TIMEOUT;
  	halIntState_t intState;

	HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.

	srch = timer_head.next;
	if (srch == &timer_head)  // No timers
	{
		nextTimeout = 0;
	}
	else
	{
		while (srch != &timer_head)
		{
			if (MEOS_TIMER_TIMEOUT32(srch) < nextTimeout)
			{
				nextTimeout = MEOS_TIMER_TIMEOUT32(srch);
			}
			// Check next timer
			srch = srch->next;
		}
	}

	HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.

  	return nextTimeout;
}

